#include "common.h"
#include "host.h"
#include "pmem.h"
#include "mmio.h"
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
static uint8_t pmem[MSIZE] = {};
uint8_t* guest_to_host(paddr_t paddr) { return pmem + paddr - MBASE; }
paddr_t host_to_guest(uint8_t *haddr) { return haddr - pmem + MBASE; }

static word_t pmem_read(paddr_t addr, int len) {
	word_t ret = host_read(guest_to_host(addr), len);
	return ret;
}
static void pmem_write(paddr_t addr, int len, word_t data) {
	host_write(guest_to_host(addr), len, data);
}
static void out_of_bound(paddr_t addr) {
	assert(0);
	//panic("address = " FMT_PADDR " is out of bound of pmem [" FMT_PADDR ", " FMT_PADDR "] at pc = " FMT_WORD,
	//addr, PMEM_LEFT, PMEM_RIGHT, cpu.pc);
}


void init_mem() {
	uint32_t *p = (uint32_t *)pmem;
	int i;
	srand(time(0));
	for(i=0;i<(int)(MSIZE/sizeof(p[0]));i++){
		//p[i]=rand();
		p[i]=0;
	}
	//Log("physical memory area [" FMT_PADDR ", " FMT_PADDR "]", PMEM_LEFT, PMEM_RIGHT);
}
word_t paddr_read(paddr_t addr, int len) {
if (in_pmem(addr)) return pmem_read(addr, len);
#ifdef CONFIG_DEVICE
return mmio_read(addr,len);
printf("why im here");
#endif
out_of_bound(addr);
return 0;
}
void paddr_write(paddr_t addr, int len, word_t data) {
if (in_pmem(addr)) { pmem_write(addr, len, data); return; }
#ifdef CONFIG_DEVICE
	mmio_write(addr,len,data); return;
#endif
out_of_bound(addr);
}   

//used for DPI-C in verilog
//extern "C" void pmem_read_pc(long long raddr, long long *rdata,char *valid,char en){
//		//printf("readpc %llx\n",raddr);
//		if(en !=0){
//			*rdata = paddr_read(raddr,8);
//			*valid = 1;
//		}
//		else{
//			*rdata = 0;
//			*valid = 0;
//		}
//}
//infact you can ignore the rmask.just rm the code after "//else{ *rdata = paddr_read(raddr,8);"
//it is just use for AM
/*extern "C" void pmem_read_hard(long long raddr, long long *rdata,char rmask,char read_en){
		//printf("en = %d\n",read_en);
		//printf("addr = %llx\n",raddr);
		if(read_en == 0){*rdata = 0;}
		//else{ *rdata = paddr_read(raddr,8);}
		else{
		//printf("read address = %llx\n",raddr);
		switch(rmask&0xff){
						case 0xff:*rdata = paddr_read(raddr,8);break;
						case 0x0f:*rdata = paddr_read(raddr,4);break;
					case 0x03:*rdata = paddr_read(raddr,2);break;
						case 0x01:*rdata = paddr_read(raddr,1);break;
						default:assert(0); break;
		}
		}
}*/
extern "C" void pmem_read_hard(long long raddr, long long *rdata,char *valid,char read_en){
//		printf("en = %d\n",read_en);
	//	printf("addr = %llx\n",raddr);
		if(read_en == 0){*rdata = 0; *valid =0;}
		//else{ *rdata = paddr_read(raddr,8);}
		else{
		//printf("read address = %llx\n",raddr);
			*rdata = paddr_read(raddr>>3<<3,8);
			*valid = 1;
		}
}
void sdb_exit();
extern "C" void pmem_write_hard(long long waddr, long long wdata,char wmask,char* w_end,char write_en){
	if(write_en != 0){
		switch(wmask&0xff){
						case 0xff:paddr_write((waddr>>3<<3),8,wdata);break;
						case 0x0f:paddr_write((waddr>>3<<3),4,wdata&0x00000000ffffffff);break;
						case 0xf0:paddr_write((waddr>>3<<3)+4,4,(wdata&0xffffffff00000000)>>32);break;
						case 0x03:paddr_write((waddr>>3<<3),2,wdata&0x000000000000ffff);break;
						case 0x0c:paddr_write((waddr>>3<<3)+2,2,(wdata&0x00000000ffff0000)>>16);break;
						case 0x30:paddr_write((waddr>>3<<3)+4,2,(wdata&0x0000ffff00000000)>>32);break;
						case 0xc0:paddr_write((waddr>>3<<3)+6,2,(wdata&0xffff000000000000)>>48);break;
						case 0x01:paddr_write((waddr>>3<<3),1,wdata&0x00000000000000ff);break;
						case 0x02:paddr_write((waddr>>3<<3)+1,1,(wdata&0x000000000000ff00)>>8);break;
						case 0x04:paddr_write((waddr>>3<<3)+2,1,(wdata&0x0000000000ff0000)>>16);break;
						case 0x08:paddr_write((waddr>>3<<3)+3,1,(wdata&0x00000000ff000000)>>24);break;
						case 0x10:paddr_write((waddr>>3<<3)+4,1,(wdata&0x000000ff00000000)>>32);break;
						case 0x20:paddr_write((waddr>>3<<3)+5,1,(wdata&0x0000ff0000000000)>>40);break;
						case 0x40:paddr_write((waddr>>3<<3)+6,1,(wdata&0x00ff000000000000)>>48);break;
						case 0x80:paddr_write((waddr>>3<<3)+7,1,(wdata&0xff00000000000000)>>56);break;
						default:sdb_exit();assert(0); break;
		}
		*w_end = 1;
		}
	else {
		*w_end = 0;
	}
	}
